/*
 * Copyright 2002-2006 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package blms.domain;

import java.util.Comparator;
import java.util.Date;

import blms.exception.ValidationException;
import blms.util.BLMSMessages;
import blms.util.LocalizedDataFormat;

/**
 * The base contest in the BLMS billiard system.
 * 
 * @author João Arthur
 * @author Jonhnny Weslley
 * @author Thiago Emmanuel
 * @version 1.00, 22/04/2008
 * @since 1.0
 */
public class Match extends Entity {

	private static final long serialVersionUID = -6426190316345205465L;

	private static final String PLAYER_DESCRIPTION_FORMAT = "date=%s,opponent=%s,myScore=%d,opponentScore=%d,myRun=%d,opponentRun=%d,myHandicap=%d,winStatus=%s";

	public static final Comparator<Match> BY_DATE_COMPARATOR = new MatchDateComparator();

	private League league;// FIXME REMOVE
	private User winner;
	private User loser;
	private Date matchDate;
	private int length;
	private int score;
	private int longestRunForWinner;
	private int longestRunForLoser;

	/**
	 * A default Constructor for a Match.
	 */
	public Match() {
		// Default constructor
	}

	/**
	 * Indicates whether an user is the winner of this <code>Match</code>.
	 * 
	 * @param user
	 *            the user to be verified whether he is the winner of this
	 *            <code>Match</code>.
	 * @return true if the specified user is the winner of this match; false
	 *         otherwise.
	 */
	public boolean isTheWinner(final User user) {
		return getWinner().equals(user);
	}

	/**
	 * Indicates whether an user is the loser of this <code>Match</code>.
	 * 
	 * @param user
	 *            the user to be verified whether he is the loser of this
	 *            <code>Match</code>.
	 * @return true if the specified user is the loser of this match; false
	 *         otherwise.
	 */
	public boolean isTheLoser(final User user) {
		return getLoser().equals(user);
	}

	/**
	 * Indicates whether an user is on the <code>Match</code>.
	 * 
	 * @param user
	 *            the user to be verified the presence on the <code>Match</code>.
	 * @return true if the specified user is on the <code>Match</code>; false
	 *         otherwise,
	 */
	public boolean isOnTheMatch(final User user) {
		return (getWinner().equals(user) || getLoser().equals(user));
	}

	public String getWinnerDescription() {
		return getPlayerDescription(LocalizedDataFormat
				.formatDate(getMatchDate()), getLoser().getName(), getLength(),
				getScore(), getLongestRunForWinner(), getLongestRunForLoser(),
				0, true);
	}

	public String getLoserDescription() {
		return getPlayerDescription(LocalizedDataFormat
				.formatDate(getMatchDate()), getWinner().getName(), getScore(),
				getLength(), getLongestRunForLoser(), getLongestRunForWinner(),
				200, false);
	}

	private String getPlayerDescription(final Object... args) {
		return String.format(PLAYER_DESCRIPTION_FORMAT, args);
	}

	public void validate() throws ValidationException {
		
		if (getWinner().equals(getLoser())) {
			throw new ValidationException(
					BLMSMessages.USERS_MUST_BE_DIFFERENT_MESSAGE);
		}

		final int length = getLength();
		if ((length < 1) && (length != Integer.MIN_VALUE)) {
			throw new ValidationException(BLMSMessages.INVALID_MATCH_LENGTH);
		}

		final int score = getScore();
		if (((score < 0) || (score >= length)) && (score != Integer.MIN_VALUE)) {
			throw new ValidationException(BLMSMessages.INVALID_SCORE);
		}

		final int longestRunForWinner = getLongestRunForWinner();
		if ((longestRunForWinner != Integer.MIN_VALUE)
				&& ((1 > longestRunForWinner) || (longestRunForWinner > length))) {
			throw new ValidationException(BLMSMessages.INVALID_RUN);
		}

		final int longestRunForLoser = getLongestRunForLoser();
		if ((longestRunForLoser != Integer.MIN_VALUE)
				&& ((0 > longestRunForLoser) || (longestRunForLoser > score))) {
			throw new ValidationException(BLMSMessages.INVALID_RUN);
		}
	}

	/**
	 * Returns the league of this <code>Match</code>.
	 * 
	 * @return the league of this <code>Match</code>.
	 */
	public League getLeague() {
		return league;
	}

	/**
	 * Sets the league of this <code>Match</code>.
	 * 
	 * @param league
	 *            the new league of this <code>Match</code>.
	 */
	public void setLeague(final League league) {
		this.league = league;
	}

	/**
	 * Returns the winner of this <code>Match</code>.
	 * 
	 * @return the winner of this <code>Match</code>.
	 */
	public User getWinner() {
		return winner;
	}

	/**
	 * Sets the winner of this <code>Match</code>.
	 * 
	 * @param winner
	 *            the new winner of this <code>Match</code>.
	 */
	public void setWinner(final User winner) {
		this.winner = winner;
	}

	/**
	 * Returns the loser of this <code>Match</code>.
	 * 
	 * @return the loser of this <code>Match</code>.
	 */
	public User getLoser() {
		return loser;
	}

	/**
	 * Sets the loser of this <code>Match</code>.
	 * 
	 * @param loserthe
	 *            new loser of this <code>Match</code>.
	 */
	public void setLoser(final User loser) {
		this.loser = loser;
	}

	/**
	 * Returns the date when this <code>Match</code> occurred.
	 * 
	 * @return the date when this <code>Match</code> occurred.
	 */
	public Date getMatchDate() {
		return matchDate;
	}

	/**
	 * Sets the date when this <code>Match</code> occurred.
	 * 
	 * @param matchDate
	 *            the date when this <code>Match</code> occurred.
	 */
	public void setMatchDate(final Date matchDate) {
		this.matchDate = matchDate;
	}

	/**
	 * Returns the length of this <code>Match</code>.
	 * 
	 * @return the length of this <code>Match</code>.
	 */
	public int getLength() {
		return length;
	}

	/**
	 * Sets the length of this <code>Match</code>.
	 * 
	 * @param length
	 *            the length of this <code>Match</code>.
	 */
	public void setLength(final int length) {
		this.length = length;
	}

	/**
	 * Returns the score of this <code>Match</code>.
	 * 
	 * @return the score of this <code>Match</code>.
	 */
	public int getScore() {
		return score;
	}

	/**
	 * Sets the score of this <code>Match</code>.
	 * 
	 * @param score
	 *            the score of this <code>Match</code>.
	 */
	public void setScore(final int score) {
		this.score = score;
	}

	/**
	 * Returns the longest run for the winner of this <code>Match</code>.
	 * 
	 * @return the longest run for the winner of this <code>Match</code>.
	 */
	public int getLongestRunForWinner() {
		return longestRunForWinner;
	}

	/**
	 * Sets the longest run for the winner of this <code>Match</code>.
	 * 
	 * @param the
	 *            longest run for the winner of this <code>Match</code>.
	 */
	public void setLongestRunForWinner(final int longestRunForWinner) {
		this.longestRunForWinner = longestRunForWinner;
	}

	/**
	 * Returns the longest run for the loser of this <code>Match</code>.
	 * 
	 * @return the longest run for the loser of this <code>Match</code>.
	 */
	public int getLongestRunForLoser() {
		return longestRunForLoser;
	}

	/**
	 * Sets the longest run for the loser of this <code>Match</code>.
	 * 
	 * @param the
	 *            longest run for the loser of this <code>Match</code>.
	 */
	public void setLongestRunForLoser(final int longestRunForLoser) {
		this.longestRunForLoser = longestRunForLoser;
	}

	/**
	 * 
	 * This static class is responsible for comparing two match dates.
	 * 
	 * @author João Arthur
	 * @author Jonhnny Weslley
	 * @author Thiago Emmanuel
	 * 
	 */
	public static class MatchDateComparator implements Comparator<Match> {
		public int compare(final Match o1, final Match o2) {
			return o1.getMatchDate().compareTo(o2.getMatchDate());
		}
	}

	/**
	 * This static class is responsible for building a Match.
	 * 
	 * @author João Arthur
	 * @author Jonhnny Weslley
	 * @author Thiago Emmanuel
	 * 
	 */
	public static class Builder {

		private final Match match;

		/**
		 * Constructs a new Builder for the match.
		 * 
		 * @param league
		 *            the league of the match.
		 */
		public Builder(final League league) {
			match = new Match();
			match.setLeague(league);
		}

		/**
		 * Sets the winner of the match.
		 * 
		 * @param winner
		 *            the winner of the match.
		 * @param longestRunForWinner
		 *            the longest run for the specified winner of this match.
		 * @return a Builder for the match.
		 */
		public Builder winner(final User winner, final int longestRunForWinner) {
			match.setWinner(winner);
			match.setLongestRunForWinner(longestRunForWinner);
			return this;
		}

		/**
		 * Sets the loser of the match.
		 * 
		 * @param loser
		 *            the winner of the match.
		 * @param longestRunForLoser
		 *            the longest run for the specified loser of this match.
		 * @return a Builder for the match.
		 */
		public Builder loser(final User loser, final int longestRunForLoser) {
			match.setLoser(loser);
			match.setLongestRunForLoser(longestRunForLoser);
			return this;
		}

		/**
		 * Sets the score of the match.
		 * 
		 * @param score
		 *            the score of the match.
		 * @return a Builder for the match.
		 */
		public Builder withScore(final int score) {
			match.setScore(score);
			return this;
		}

		/**
		 * Sets the length of the match.
		 * 
		 * @param length
		 *            the length of the match.
		 * @return a Builder for the match.
		 */
		public Builder withLength(final int length) {
			match.setLength(length);
			return this;
		}

		/**
		 * Constructs the match with the specified date.
		 * 
		 * @param date
		 *            the date of the match.
		 * @return The match created.
		 * @throws ValidationException
		 */
		public Match on(final Date date) {
			match.setMatchDate(date);
			return match;
		}

	}

}